/**
 * 
 */
package edu.gtcfla.km.security.realm;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.permission.PermissionResolver;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.MutablePrincipalCollection;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.vsg.biz.account.Role;
import org.vsg.biz.account.User;
import org.vsg.biz.account.repositories.RoleRepo;
import org.vsg.biz.account.repositories.UserRepo;
import org.vsg.security.ResourcesPermission;
import org.vsg.security.checker.ResourcesPermissionChecker;
import org.vsg.security.strategy.MenuPermissionRule;

import edu.gtcfla.km.security.PasswordErrorException;
import edu.gtcfla.km.security.RoleStrategyRule;
import edu.gtcfla.km.security.api.AccountPrincipalConstant;

/**
 * @author Bill vison
 *
 */
public class AccountUserAuthenticatingRealm extends AuthorizingRealm {


	private final static Logger logger = LoggerFactory.getLogger(AccountUserAuthenticatingRealm.class);	

	/*
	@Resource
	private AccountUserDao accountUserDao;

	@Resource
	private PermissionDao permissionDao;
	*/
	
	@Autowired(required=false)
	private UserRepo userRepo;
	
	@Autowired(required=false)
	private RoleRepo roleRepo;
	
	@Autowired(required=false)
	private MenuPermissionRule menuPermissionRule;

	@Autowired(required=false)
	private RoleStrategyRule roleStrategyRule;
	
	
	// --- load permission strategy ----
	@Autowired(required=false)
	private Set<ResourcesPermissionChecker> permissionCheckerSet;

	
	

	public void setPermissionCheckerSet(
			Set<ResourcesPermissionChecker> permissionCheckerSet) {
		this.permissionCheckerSet = permissionCheckerSet;
	}

	/* (non-Javadoc)
	 * @see org.apache.shiro.authz.Authorizer#checkPermission(org.apache.shiro.subject.PrincipalCollection, org.apache.shiro.authz.Permission)
	 */
	@Override
	public void checkPermission(PrincipalCollection arg0, Permission arg1)
			throws AuthorizationException {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see org.apache.shiro.authz.Authorizer#checkPermissions(org.apache.shiro.subject.PrincipalCollection, java.lang.String[])
	 */
	@Override
	public void checkPermissions(PrincipalCollection arg0, String... arg1)
			throws AuthorizationException {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see org.apache.shiro.authz.Authorizer#checkPermissions(org.apache.shiro.subject.PrincipalCollection, java.util.Collection)
	 */
	@Override
	public void checkPermissions(PrincipalCollection arg0,
			Collection<Permission> arg1) throws AuthorizationException {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see org.apache.shiro.authz.Authorizer#checkRole(org.apache.shiro.subject.PrincipalCollection, java.lang.String)
	 */
	@Override
	public void checkRole(PrincipalCollection arg0, String arg1)
			throws AuthorizationException {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see org.apache.shiro.authz.Authorizer#checkRoles(org.apache.shiro.subject.PrincipalCollection, java.util.Collection)
	 */
	@Override
	public void checkRoles(PrincipalCollection arg0, Collection<String> arg1)
			throws AuthorizationException {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see org.apache.shiro.authz.Authorizer#checkRoles(org.apache.shiro.subject.PrincipalCollection, java.lang.String[])
	 */
	@Override
	public void checkRoles(PrincipalCollection arg0, String... arg1)
			throws AuthorizationException {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see org.apache.shiro.authz.Authorizer#hasAllRoles(org.apache.shiro.subject.PrincipalCollection, java.util.Collection)
	 */
	@Override
	public boolean hasAllRoles(PrincipalCollection arg0, Collection<String> arg1) {
		// TODO Auto-generated method stub
		return false;
	}

	/* (non-Javadoc)
	 * @see org.apache.shiro.authz.Authorizer#hasRole(org.apache.shiro.subject.PrincipalCollection, java.lang.String)
	 */
	@Override
	public boolean hasRole(PrincipalCollection pricipalCollection, String definedRole) {
		// TODO Auto-generated method stub
		Collection sysRoles = pricipalCollection.fromRealm( AccountPrincipalConstant.SYSTEM_ROLES );
		
		boolean existed = sysRoles.contains( definedRole );
		
		return existed;
	}

	/* (non-Javadoc)
	 * @see org.apache.shiro.authz.Authorizer#hasRoles(org.apache.shiro.subject.PrincipalCollection, java.util.List)
	 */
	@Override
	public boolean[] hasRoles(PrincipalCollection arg0, List<String> arg1) {
		// TODO Auto-generated method stub
		return null;
	}

	/* (non-Javadoc)
	 * @see org.apache.shiro.authz.Authorizer#isPermitted(org.apache.shiro.subject.PrincipalCollection, java.lang.String)
	 */
	@Override
	public boolean isPermitted(PrincipalCollection principal, String permStr) {
		// TODO Auto-generated method stub
		
		PermissionResolver permResolver = this.getPermissionResolver();
		
		ResourcesPermission permission = (ResourcesPermission)permResolver.resolvePermission( permStr );
		
		return isPermitted(principal , permission);
	}

	
	private Set<Role> findAssignedRolesByUser(User user ) {
		/*
		CacheManager cacheManager = this.getCacheManager();
		Cache<String,Set<Role>> cache = cacheManager.getCache("user:" + user.getUserId()); 
		Set<Role>  assignedRoles = (Set<Role>)cache.get("user_assigned_roles");
		
		if (assignedRoles == null) {
			AccountUser newAccountUser = accountUserDao.getEntityWithRoles( user.getUserId() );
			assignedRoles = newAccountUser.getAssignedRoles();			
		}
		
		// --- update cache ---
		if ( assignedRoles != null) {
			cache.put("user_assigned_roles", assignedRoles);
		}
		
		return assignedRoles;
		*/
		return null;
	}
	
	
	private Set<Permission> findAllPermissionByRoles( User user , Set<Role> assignedRoles , String resType) {
		/*
		CacheManager cacheManager = this.getCacheManager();
		Cache<String,Set<org.vsg.crm.sysaccount.domain.Permission>> cache = cacheManager.getCache("user:" + user.getUserId()); 
		Set<org.vsg.crm.sysaccount.domain.Permission> allPerms = (Set<org.vsg.crm.sysaccount.domain.Permission>)cache.get( "roles_assigned_perms" );
		
		Set<org.vsg.crm.sysaccount.domain.Permission> allAssignedPerms = new LinkedHashSet<org.vsg.crm.sysaccount.domain.Permission>();
		
		if (allPerms == null) {
			
			for (Role role : assignedRoles) {
				Set<org.vsg.crm.sysaccount.domain.Permission> assignedPermissions 
					= permissionDao.findEnabledPermissionsByRole(role , resType);
				allAssignedPerms.addAll( assignedPermissions );
			}			
		}
	
		return allAssignedPerms;
		*/
		return null;
	}

	/* (non-Javadoc)
	 * @see org.apache.shiro.authz.Authorizer#isPermitted(org.apache.shiro.subject.PrincipalCollection, org.apache.shiro.authz.Permission)
	 */
	@Override
	public boolean isPermitted(PrincipalCollection principal, Permission permission) {

		// --- check resource permssion type and choise the permission type ---
		ResourcesPermission resPerm = (ResourcesPermission)permission;
		
		boolean isPermitted = false;
		
		String fullResource = resPerm.getFullpathResource();
		String resType = resPerm.getResType();
		
		// --- menu permission rule ---
		String resourceId = null;
		if (resType.equals("mnu")) {
			resourceId = ResourcesPermission.Helper.recovertResourceId( fullResource );
			
			// --- get permission form permission storage ---
			menuPermissionRule.setSubjectPrincipalCollection((MutablePrincipalCollection) principal);
			Map<String , Permission>  accountPerms = menuPermissionRule.process();
			
			if (!accountPerms.isEmpty() ) {
				Permission perm  = accountPerms.get( resourceId );
				
				isPermitted = permission.implies( perm );
			}
		
		}
		
		
		return isPermitted;
		
/*
		if (resType.equals("url") || resType.equals("purl")) {
			return true;
		}
		
		
		// --- load permission ---
		Collection accountList = principal.fromRealm( AccountPrincipalConstant.ACCOUNT );
		
		AccountUser user = (AccountUser) new Vector(accountList).get(0);
		
		// --- find ----
		Set<Role> assingedRoles = findAssignedRolesByUser(user);
		
		Set<org.vsg.crm.sysaccount.domain.Permission> assignedPermission = findAllPermissionByRoles(user , assingedRoles , resType);

		// --- check the permission is permitted ---
		for (org.vsg.crm.sysaccount.domain.Permission perm : assignedPermission) {
			// --- build permission ---
			Set<ResourcesPermission> userAssignedPermissions = buildResourcesPermission(perm);
			
			for (ResourcesPermission userAssignedPerm : userAssignedPermissions) {
				
				boolean implies = resPerm.implies( userAssignedPerm );
				if (implies) {
					return true;
				}
			}

		}

		return false;
		*/
	}
	
	private Set<ResourcesPermission> buildResourcesPermission(Permission perm) {

		return null;
	}
	
	

	/* (non-Javadoc)
	 * @see org.apache.shiro.authz.Authorizer#isPermitted(org.apache.shiro.subject.PrincipalCollection, java.lang.String[])
	 */
	@Override
	public boolean[] isPermitted(PrincipalCollection arg0, String... arg1) {
		// TODO Auto-generated method stub
		logger.info("isPermitted(PrincipalCollection arg0, String... arg1)");			
		return null;
	}

	/* (non-Javadoc)
	 * @see org.apache.shiro.authz.Authorizer#isPermitted(org.apache.shiro.subject.PrincipalCollection, java.util.List)
	 */
	@Override
	public boolean[] isPermitted(PrincipalCollection arg0, List<Permission> arg1) {
		// TODO Auto-generated method stub
		logger.info("isPermitted(PrincipalCollection arg0, List<Permission> arg1)");
		return null;
	}

	/* (non-Javadoc)
	 * @see org.apache.shiro.authz.Authorizer#isPermittedAll(org.apache.shiro.subject.PrincipalCollection, java.lang.String[])
	 */
	@Override
	public boolean isPermittedAll(PrincipalCollection arg0, String... arg1) {
		// TODO Auto-generated method stub
		logger.info("isPermittedAll(PrincipalCollection arg0, List<Permission> arg1)");
		return false;
	}

	/* (non-Javadoc)
	 * @see org.apache.shiro.authz.Authorizer#isPermittedAll(org.apache.shiro.subject.PrincipalCollection, java.util.Collection)
	 */
	@Override
	public boolean isPermittedAll(PrincipalCollection arg0,
			Collection<Permission> arg1) {
		// TODO Auto-generated method stub
		logger.info("isPermittedAll(PrincipalCollection arg0, List<Permission> arg1)");
		return false;
	}

	/* (non-Javadoc)
	 * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken authcToken) throws AuthenticationException {
		// TODO Auto-generated method stub
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
		
		AuthenticationInfo authenInfo = null;

		try {
			Iterable<User>  findingUsers = userRepo.findAllByName( token.getUsername() );
			
			boolean haveOneMatchPassword = false;
			
			User loginUser = null;
			for (Iterator<User> iterUser = findingUsers.iterator() ; iterUser.hasNext() ; ) {
				loginUser = iterUser.next();
				if ( loginUser.getPassword().equals( new String( token.getPassword() ) ) ) {
					haveOneMatchPassword = true;
					break;
				}			
			}

			if (loginUser == null) {
				throw new NoSuchElementException("Could not found username '"+ token.getUsername()  +"' ");
			}

			// --- validate password ---
			if ( !haveOneMatchPassword ) {
				throw new PasswordErrorException("password is not correct! ");
			}
			
			
			//AccountUser newAccountUser =  accountUserDao.getEntityWithRoles( loginUser.getUserId() );
			// --- set this asscount principals ----
			SimplePrincipalCollection spc = new SimplePrincipalCollection();
			spc.add( loginUser.getName() , AccountPrincipalConstant.REALMNAME_USER_NAME );
			spc.add( loginUser.getId(), AccountPrincipalConstant.REALMNAME_USER_ID);
			spc.add( loginUser , AccountPrincipalConstant.ACCOUNT );
			
			
			//  --- role ---
			
			Set<Role> assignedRoles = this.roleStrategyRule.findActiveRoles(spc);
			
			Set<String> systemRoles = new LinkedHashSet<String>();
			
			Set<String> roleIds = new LinkedHashSet<String>();
			for (Role role : assignedRoles) {
				roleIds.add( role.getId() );
				systemRoles.add( role.getRole() );
			}

			if ( !systemRoles.isEmpty() ) {
				spc.addAll( systemRoles , AccountPrincipalConstant.SYSTEM_ROLES);
			}			

			authenInfo = new SimpleAuthenticationInfo(  spc, loginUser.getPassword());


		} catch (DataAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchElementException e) {

			logger.debug("Could not find data , " +  e.getLocalizedMessage() );
			
			throw new UnknownAccountException("Account user ["+token.getUsername()+"] doest not exist in system! ");
		}		
		
		return authenInfo;
	}

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		
		
		
		System.out.println("run permission ---");

		return null;
	}

	
	
}
